// HttpServer.hpp
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <pthread.h>
#include "TcpSocket.hpp"

const std::string wwwroot = "./wwwroot"; // web根目录
// ./加不加都行，不加默认在当前路径下找。另外，这个目录也可以写到配置文件里，动态地去指明
const std::string sep = "\r\n";
const std::string homepage = "index.html";

static const int defaultport = 8080;

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *ts) : sockfd(fd), httpsvr(ts)
    {
    }

public:
    int sockfd;
    HttpServer *httpsvr;
};

class HttpRequest
{
public:
    // 反序列化
    void Deserialize(const std::string &req)
    {
        std::size_t linestart = 0; // 标记一行的起始位置
        while (true)
        {
            std::size_t pos = req.find(sep, linestart); // 返回值是\r的下标
            if (pos == std::string::npos)
                break;

            std::string temp = req.substr(linestart, pos - linestart); // 提取子串，一行
            linestart = pos + sep.size();                              // 让linestart指向下一行起始位置
            if (temp.empty())
                break; // 如果是空行，说明后面是正文，就不存进数组了

            req_Header.push_back(temp); // 把这一行放进数组里
        }
        text = req.substr(linestart); // 把正文内容放进text里
    }
    // 请求行解析
    void Parse()
    {
        std::stringstream ss(req_Header[0]);
        ss >> method >> url >> http_version;

        // 对URL做处理
        // 1.先确定web根目录
        file_path = wwwroot; // "./wwwroot"
        // 2.追加用户请求的目录，注意url后面可能带参数
        std::size_t pos = url.find("?"); // 如果有参数，就找到参数的起始位置，也就是?的下标
        if (pos != std::string::npos)
        {
            parameter = url.substr(pos + 1); // 把参数部分单独存放
            file_path += url.substr(0, pos); // "./wwwroot/xxx/"
        }
        else
        {
            file_path += url; // "./wwwroot/xxx/"
        }
        // 3.如果请求的路径最后是一个/，就把请求路径追加一个index.html
        if (file_path[file_path.size() - 1] == '/')
            file_path += homepage; // "./wwwroot/xxx/index.html"
    }
    void DebugPrint()
    {
        std::cout << "-----------------------------------" << std::endl;
        for (auto &line : req_Header)
        {
            std::cout << line << std::endl;
        }

        std::cout << std::endl;
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << "parameter: " << parameter << std::endl;

        std::cout << std::endl
                  << text << std::endl;
        std::cout << "-----------------------------------" << std::endl;
    }

public:
    std::vector<std::string> req_Header; // 保存报头的行
    std::string text;                    // 保存正文

    // 请求行解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; // uel处理之后完整的访问路径
    std::string parameter; // url里的参数部分
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport)
        : _port(port)
    {
    }
    bool Start()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);
            if (sockfd < 0)
                continue;
            lg(Info, "get a new connect, sockfd: %d", sockfd);

            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->httpsvr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }
    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 无法保证读到一个完整报文
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer; // 收到什么就打印出什么

            // 假设我们读取到的就是一个完整的、独立的http请求
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            req.DebugPrint();

            // 返回响应
            std::string text = ReadHtmlContent(req.file_path); // 正文
            std::string response_line = "HTTP/1.0 200 OK\r\n"; // 状态行
            std::string response_header = "Content-Length: ";  // 响应报头
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            std::string blank_line = "\r\n"; // 空行

            std::string response = response_line; // 整个报文
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    std::string ReadHtmlContent(const std::string &htmlpath)
    {
        std::ifstream in(htmlpath);
        if (!in.is_open())
        {
            return "404";
        }

        std::string content;
        std::string line;
        while (std::getline(in, line))
        {
            content += line;
        }

        in.close();
        return content;
    }
    ~HttpServer()
    {
    }

private:
    TcpSocket _listensock;
    uint16_t _port;
};
